/*
 * Syncrem - Tool for remote synchronizing
 *
 * Copyright (c) 2011 Norbert Chromek <norbert.chromek@gmail.com>
 *
 * Syncrem is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Syncrem is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Syncrem. If not, see <http://www.gnu.org/licenses/>.
 */

#include "filelock.h"

#include <cstdio>
#include <cstring>

#include <fcntl.h>
#include <unistd.h>

// initialize static members
const std::string FileLock::LOCK_PATH = "/var/lock/";
const std::string FileLock::LOCK_FILE = "syncrem.lck";


/**
 * Constructor.
 */

FileLock::FileLock() :
	isMain_(false),
	status_(FileLock::Error),
	lockFile_(-1) {

	openLock();
}

/**
 * Destructor.
 */

FileLock::~FileLock() {
	closeLock();
}

/**
 * This function checks whether lock file is already locked.
 * Result is written into status_ variable.
 */

void FileLock::checkLock() {
	struct flock lock;
	memset(&lock, 0, sizeof(struct flock));
	if(fcntl(lockFile_, F_GETLK, &lock) == -1) {
		status_ = FileLock::Error;
	} else {
		if(lock.l_type == F_UNLCK)
			status_ = FileLock::Unlocked;
		else
			status_ = FileLock::Locked;
	}
}
		
/**
 * This functions closes lock file and deletes it
 * if lock is in daemon process.
 */

void FileLock::closeLock() {
	if(lockFile_ != -1)
		close(lockFile_);
	if(isMain_)
		unlink((LOCK_PATH + LOCK_FILE).c_str());
}

/**
 * This function tries to lock file. Should be called when main process is forked.
 * 
 * @return True if locking is successful.
 */

bool FileLock::lock() {
	if(status_ != FileLock::Error) {
		struct flock lock;
		memset(&lock, 0, sizeof(struct flock));
		lock.l_type = F_WRLCK;

		if(fcntl(lockFile_, F_SETLK, &lock) != -1) {
			status_ = FileLock::Locked;
			return true;
		}
	}
	return false;
}

/**
 * This function tries to open lock file.
 * It creates new file if necessary and writes
 * result of operation into status_ variable.
 */

void FileLock::openLock() {
	lockFile_ = open((LOCK_PATH + LOCK_FILE).c_str(), O_WRONLY | O_CREAT, 0666);
	if(lockFile_ != -1)
		checkLock();
}

/**
 * This function is used to set whether lock is in main
 * process or not.
 * 
 * @param isMain True if lock is in main process.
 */

void FileLock::setMain(bool isMain) {
	isMain_ = isMain;
}

/**
 * This function returns status of lock.
 */

FileLock::LockStatus FileLock::status() {
	return status_;
}